package com.lagou.edu.context;

import com.lagou.edu.annotation.Autowired;
import com.lagou.edu.annotation.Transactional;
import com.lagou.edu.factory.ProxyFactory;

import java.io.File;
import java.io.FileFilter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author caiwei
 * @date 2020-9-13 23:39
 */
public class ApplicationContext {
    //包名
    private String packageName;
    private ConcurrentHashMap<String, Object> beans = new ConcurrentHashMap<>();

    public ApplicationContext(String packageName) {
        this.packageName = packageName;
        initBeans();
    }

    private void initBeans() {
        List<Class> classList = getClasses(packageName);
        findClassIsAddedAnnotation(classList);
    }

    /**
     *
     * 获取包下所有的类文件
     * @author  caiwei
     * @date    2020-9-13 23:57
     * @param   [packageName]
     * @return  java.util.List<java.lang.Class>
     */
    private List<Class> getClasses(String packageName) {
        List<Class> classList = new ArrayList<>();
        String packageDirName = packageName.replace(".", "/");
        Enumeration<URL> dirs;
        try {
            dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
            while (dirs.hasMoreElements()) {
                URL url = dirs.nextElement();
                String protocol = url.getProtocol();
                if (protocol.equals("file")) {
                    String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
                    findAndAddClassesInPackageByFile(packageName, filePath, classList);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return classList;
    }

    private void findAndAddClassesInPackageByFile(String packageName, String filePath, List<Class> classList) {
        File dir = new File(filePath);
        //选出文件夹下面所有的文件
        File[] files = dir.listFiles(new FileFilter() {
            @Override
            public boolean accept(File file) {
                return (file.isDirectory() || file.getName().endsWith(".class"));
            }
        });
        for (File file : files) {
            if (file.isDirectory()) {
                findAndAddClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), classList);
            } else {
                String className = file.getName().substring(0, file.getName().length() - 6);
                try {
                    classList.add(Class.forName(packageName + "." + className));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     *
     * 查找添加了自定义的注解类
     * @author  caiwei
     * @date    2020-9-13 23:47
     * @param   [classList]
     * @return  void
     */
    private void findClassIsAddedAnnotation(List<Class> classList) {
        try{
            for (Class aClass : classList) {
                classToObjectIntoBeans(aClass, classList);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     *
     * 通过动态代理初始化对象，并存入map中
     * @author  caiwei
     * @date    2020-9-13 23:51
     * @param   [aClass, classList]
     * @return  void
     */
    private void classToObjectIntoBeans(Class aClass, List<Class> classList) {
        Object obj = null;

        try{
            if(aClass.isInterface()){
                for(Class implClass : classList) {
                    if (implClass.isInterface()) {
                        continue;
                    }
                    Class fieldClassCopy = implClass.getClassLoader().loadClass(aClass.getName());
                    if (fieldClassCopy.isAssignableFrom(implClass)) {
                        Constructor[] constructors = implClass.getConstructors();
                        for(Constructor constructor : constructors){
                            int parameterCount = constructor.getParameterCount();
                            if(parameterCount==0){
                                obj = constructor.newInstance();
                            }
                        }
                        break;
                    }
                }
            } else {
                Constructor[] constructors = aClass.getConstructors();
                for(Constructor constructor : constructors){
                    int parameterCount = constructor.getParameterCount();
                    if(parameterCount==0){
                        obj = constructor.newInstance();
                    }
                }
            }
            if (obj != null) {
                String beanId = toLowerCaseFirstOne(aClass.getSimpleName());
                beans.put(beanId, obj);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    // 首字母转小写
    private static String toLowerCaseFirstOne(String s) {
        if (Character.isLowerCase(s.charAt(0)))
            return s;
        else
            return (new StringBuilder()).append(Character.toLowerCase(s.charAt(0))).append(s.substring(1)).toString();
    }

    /**
     *
     * 获取bean并实例化同时加入事务
     * @author  caiwei
     * @date    2020-9-13 23:59
     * @param   [beanName, applicationContext]
     * @return  java.lang.Object
     */
    public Object getBean(String beanName,ApplicationContext applicationContext) {
        Object beanObject = beans.get(beanName);
        try{
            referenceBindObject(beanObject);
        }catch (Exception e){
            e.printStackTrace();
        }

        Class aClass = beanObject.getClass();
        Annotation annotation = aClass.getAnnotation(Transactional.class);
        if (annotation != null) {
            ProxyFactory proxyFactory = (ProxyFactory) applicationContext.getBean("proxyFactory", applicationContext);
            beanObject = proxyFactory.getJdkProxy(beanObject);
        }
        return beanObject;
    }

    /**
     *
     * 实例化bean
     * @author  caiwei
     * @date    2020-9-14 0:01
     * @param   [beanObject]
     * @return  java.lang.Object
     */
    private Object referenceBindObject(Object beanObject) {
        Class beanClass = beanObject.getClass();
        Field[] declaredFields = beanClass.getDeclaredFields();
        try {
            for (Field field : declaredFields) {
                if (!field.isAccessible()) {
                    field.setAccessible(true);
                }
                Autowired filedAnnotation = field.getAnnotation(Autowired.class);
                if (filedAnnotation == null) {
                    System.out.println(beanClass.getName() + "类中的" + field.getName() + "字段,该字段上没有加注解");
                    break;
                }
                Class fieldClass = field.getType();
                String classSimpleName = toLowerCaseFirstOne(fieldClass.getSimpleName());
                System.out.println("classSimpleName:"+classSimpleName);
                Object fieldObject = beans.get(classSimpleName);
                Object object = referenceBindObject(fieldObject);
                field.set(beanObject, object);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return beanObject;
    }

}
